using System;
using System.Drawing;
using System.IO;

/****************************************
 * 
 * update:2008/8/27
 * 
 * 1.Auto open and auto close
 * 2.you can config the file name and path
 * 
 *****************************************/

namespace Zivsoft.Log
{
    /// <summary>
    /// Provides logging functionality for test-specific applications.
    /// </summary>
    class XmlLogger : MarshalByRefObject, ILog
    {

        private const string RootElementName="Log";
        private const string Header = "<?xml-stylesheet type=\"text/xsl\" href=\"Log.xslt\"?>";
        private StreamWriter _streamWriter;
        private static FileStream _fileStream;
        private static bool _currentState;
        private readonly string _xmlLogName;
        
        private static int _screenshotCount;

        // customizable values
        private readonly LogLevel _logLevel;

        /// <summary>
        /// The static Logger constructor creates an XML file with a randomly
        /// generated name, and writes the log header data into the file.
        /// </summary>
        /// <remarks>The XML structure will capture the calls to all libraries,
        /// in a call stack fashion.  It will also capture the computer's memory
        /// and drive usage before and after the test run.
        /// </remarks>
        public XmlLogger()
        {
            _xmlLogName = LogConfig.Single().XmlLogName;
            _logLevel = LogConfig.Single().LogLevel;
            _currentState = false;
        }


        public XmlLogger(LogLevel logLevel,string logFile)
        {
            _xmlLogName = logFile;
            _logLevel = logLevel;
            _currentState = false;
        }


        /// <summary>
        /// Captures a screenshot, and marks the image file name in the log.
        /// </summary>
        /// <param name="screenshotName">The desired file name of the image captured.  If empty,
        /// a value will be generated.</param>
        private void LogScreenshot(string screenshotName)
        {
            Image desktop = CaptureScreen.DesktopImage();

            if (desktop != null)
            {
                if (screenshotName.Length == 0)
                {
                    screenshotName = "Log_ScreenShot_" + _screenshotCount++ + ".jpg";
                }
                else if (!screenshotName.ToLower().EndsWith(".jpg"))
                {
                    screenshotName += ".jpg";
                }
                try
                {
                    desktop.Save(screenshotName);
                }
                catch
                {
                    screenshotName = "Log_ScreenShot_" + _screenshotCount++ + ".jpg";
                    desktop.Save(screenshotName);
                }
            }
            else
            {
                screenshotName = "FAILED TO CAPTURE SCREEN SHOT.";
            }

            WriteLog("Screenshot");
            WriteLog("<![CDATA[" + screenshotName + "]]>");
            WriteLog("Screenshot");
        }


        /// <summary>
        /// Writes a number of elements, containing data collected from the system.
        /// </summary>
        private void WriteMachineInfo()
        {
            try
            {
                const string MachineInfoTag = "MachineInfo";
                WriteLog("<{0}>", MachineInfoTag);
                WriteLog(string.Format("<{0}>{1}</{0}>", "MachineName", MachineInfo.MachineName));
                WriteLog(string.Format("<{0}>{1}</{0}>", "MachineIP", MachineInfo.IP));
                WriteLog(string.Format("<{0}>{1}</{0}>", "HarddriveTotalSpace", MachineInfo.HardDriveSize));
                WriteLog(string.Format("<{0}>{1}</{0}>", "HarddriveFreeSpace", MachineInfo.HardDriveFreeSpace));
                WriteLog(string.Format("<{0}>{1}</{0}>", "OperatingSystemVersion", MachineInfo.OSVersion));
                WriteLog(string.Format("<{0}>{1}</{0}>", "OperatingSystemLanguage", MachineInfo.OSLanguage));
                WriteLog(string.Format("<{0}>{1}</{0}>", "LoggerVersion", MachineInfo.LoggerVersion));
                WriteLog("</{0}>", MachineInfoTag);
            }
            catch (Exception e)
            {
                Factory.FileLog.LogError("{0}", e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        private void WriteLog(string message, params object[] args)
        {
            if (_streamWriter == null)
            {
                Factory.FileLog.LogWarning("_streamWritter is null");
            }
            else
            {
                _streamWriter.WriteLine(message, args);
            }
        }


        private void WriteLogEntry(LogLevel level, string message, params object[] args)
        {
            WriteLog("<LogEntry type='{0}' time='{1}'>", level, DateFormat.LogDate.GetLogDateFormat(DateTime.Now));
            WriteLog("<![CDATA[{0}]]>", string.Format(message, args));
            WriteLog("</LogEntry>");
        }

        public void LogWarning(string message, params object[] args)
        {
            if (_logLevel < LogLevel.Error)
            {
                Open();
                WriteLogEntry(LogLevel.Warning, message, args);
                Close();
            }
        }

        public void LogDebug(string message, params object[] args)
        {
            if (_logLevel == LogLevel.Debug)
            {
                Open();
                WriteLogEntry(LogLevel.Debug, message, args);
                Close();
            }
        }



        /// <summary>
        /// Writes the provided data as a CDATA element into the XML file.
        /// </summary>
        /// <remarks>
        /// This method is intended to be used sparingly, for debugging notes
        /// within a given function.  It is not intended to track a libraries
        /// function call stack.
        /// </remarks>
        public void LogInfo(string message, params object[] args)
        {
            if (_logLevel < LogLevel.Warning)
            {
                Open();
                WriteLogEntry(LogLevel.Info, message, args);
                Close();
            }
        }

        /// <summary>
        /// Marks the start of a test case step in the logger.
        /// </summary>
        /// <remarks>
        /// This should only be used within a test case.
        /// </remarks>
        public void LogError(string message, params object[] args)
        {
            Open();
            WriteLogEntry(LogLevel.Error,message, args);
            //If your level is error, then capture
            if (_logLevel == LogLevel.Error)
            {
                LogScreenshot(Guid.NewGuid().ToString());
            }
            Close();
        }

        /// <summary>
        /// 
        /// </summary>
        private void Open()
        {
            lock (this)
            {
                //full path
                string path;
                if (!_xmlLogName.Contains(AppDomain.CurrentDomain.BaseDirectory))
                {
                    path = AppDomain.CurrentDomain.BaseDirectory + _xmlLogName;
                }
                else
                {
                    path = _xmlLogName;
                }

                if (!_currentState)
                {
                    if (!File.Exists(path)) //not exist
                    {

                        _fileStream = File.Open(path, FileMode.Create, FileAccess.ReadWrite);
                        _streamWriter = new StreamWriter(_fileStream);
                        WriteLog(Header);
                        WriteLog("<{0}>", RootElementName);
                        WriteMachineInfo();
                    }
                    else //exist
                    {
                        _fileStream = File.Open(path, FileMode.Open, FileAccess.ReadWrite);
                        _streamWriter = new StreamWriter(_fileStream);
                        FileAppender fa = new FileAppender(ref _streamWriter);
                        fa.BackPosition();
                    }
                    WriteLog("<Msg>");
                    _currentState = true;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void Close()
        {
            lock (this)
            {

                try
                {
                    if (_currentState)
                    {
                        WriteLog("</Msg>");
                        WriteLog("</{0}>", RootElementName);
                        try
                        {
                            _streamWriter.Close();
                        }
                        catch(Exception e)
                        {
                            Factory.FileLog.LogError("{0}",e);
                        }
                        
                        try
                        {
                            _fileStream.Close();
                        }
                        catch(Exception e)
                        {
                            Factory.FileLog.LogError("{0}",e);
                        }
                        _currentState = false;
                    }
                }
                catch (Exception e)
                {
                    Factory.FileLog.LogError("{0}", e);
                }

            }
        }
    }
}